#include <iostream>
#include <memory>

namespace {

typedef void (*fn_t)(void);

class Test
{
public:
    virtual void fn1(void)
    {
        printf("hello world 1\n");
    }

    virtual void fn2(void)
    {
        printf("hello world 2\n");
    }
};

long getVptr(Test * p)
{
     // 把对象指针转为int*，然后解引用就可以获取对象地址上前4个字节的值
    long data = *(long *)p;
    return data;
}

void callFuncInVTable(Test * p, int index, bool printFlag=false)
{
    long vptr = getVptr(p); // 获取虚指针值
    if (printFlag)
    {
        printf("vptr: 0x%08x\n", vptr);
    }


    long *vtbl = (long*)(vptr); // 解引用得到虚函数表值
    fn_t func = (fn_t)vtbl[index]; // 获取第n个函数（即fn1）的地址值，并转为函数指针
    if (printFlag)
    {
        printf("function addr: 0x%p\n", func);
    }

    func(); // 通过函数指针去调用函数
}

void Handle() {
    Test obj1, obj2;
    // 在计算类对象大小时，并不包含其拥有的函数，因为函数可以看做是一块代码单元，相同类的对象都可以使用它，
    // 在调用函数时，会传递一个隐式参数this来表示是哪个类对象在调用它。这也是为什么obj1和obj2的虚指针指向的是相同的虚函数表
    printf("obj1 size: %d; obj2 size: %d\n", sizeof(obj1), sizeof(obj2));
    printf("obj1 vptr: 0x%x\n", getVptr(&obj1));
    printf("obj2 vptr: 0x%x\n", getVptr(&obj2));

    long vptr = getVptr(&obj1); // 获取虚指针值
    long *vtbl = (long*)vptr; // 解引用得到虚函数表值

    fn_t func = (fn_t)vtbl[0]; // 获取第一个函数（即fn1）的地址值，并转为函数指针
    func(); // 通过函数指针去调用函数

    callFuncInVTable(&obj1, 0, true);
    callFuncInVTable(&obj1, 1, true);
}

}

void HandleVPtr() {
    Handle();
}